/*
 ============================================================================
 Name        : hev-sender.c
 Author      : Heiher <admin@heiher.info>
 Version     : 0.0.2
 Copyright   : Copyright (c) 2010 everyone.
 Description : Linkage 802.1x for Unix-like operating system.
 ============================================================================
 */

#include <pcap.h>
#include <string.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>

#include "hev-sender.h"

#define HEV_SENDER_BUFFER_SIZE		512
#define HEV_SENDER_TIMEOUT			500

enum
{
	PROP_0,
	PROP_INTERFACE,
	N_PROPERTIES
};

static GParamSpec * hev_sender_properties[N_PROPERTIES] = { NULL };

#define HEV_SENDER_GET_PRIVATE(obj)	(G_TYPE_INSTANCE_GET_PRIVATE((obj), HEV_TYPE_SENDER, HevSenderPrivate))

typedef struct _HevSenderPrivate HevSenderPrivate;

struct _HevSenderPrivate
{
	pcap_t * pcap;
	gchar * interface;
};

static void hev_sender_initable_iface_init(GInitableIface * iface);
static gboolean hev_sender_initable_init(GInitable * initable,
					GCancellable * cancellable, GError ** error);
static void hev_sender_set_property(GObject * obj, guint id,
				const GValue * value, GParamSpec * pspec);
static void hev_sender_get_property(GObject * obj, guint id,
				GValue * value, GParamSpec * pspec);

G_DEFINE_TYPE_WITH_CODE(HevSender, hev_sender, G_TYPE_OBJECT,
		G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, hev_sender_initable_iface_init));

static void hev_sender_dispose(GObject * obj)
{
	HevSender * self = HEV_SENDER(obj);
	HevSenderPrivate * priv = HEV_SENDER_GET_PRIVATE(self);

	G_OBJECT_CLASS(hev_sender_parent_class)->dispose(obj);
}

static void hev_sender_finalize(GObject * obj)
{
	HevSender * self = HEV_SENDER(obj);
	HevSenderPrivate * priv = HEV_SENDER_GET_PRIVATE(self);

	/* pcap START */
	if(priv->pcap)
	{
		pcap_close(priv->pcap);

		priv->pcap = NULL;
	}
	/* pcap END */

	G_OBJECT_CLASS(hev_sender_parent_class)->finalize(obj);
}

static GObject * hev_sender_constructor(GType type, guint n, GObjectConstructParam * param)
{
	return G_OBJECT_CLASS(hev_sender_parent_class)->constructor(type, n, param);
}

static void hev_sender_constructed(GObject * obj)
{
}

static void hev_sender_class_init(HevSenderClass * klass)
{
	GObjectClass * obj_class = G_OBJECT_CLASS(klass);

	obj_class->constructor = hev_sender_constructor;
	obj_class->constructed = hev_sender_constructed;
	obj_class->dispose = hev_sender_dispose;
	obj_class->finalize = hev_sender_finalize;
	obj_class->set_property = hev_sender_set_property;
	obj_class->get_property = hev_sender_get_property;

	hev_sender_properties[PROP_INTERFACE] =
			g_param_spec_string("interface", "Interface", "Network Interface",
					NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);

	g_object_class_install_properties(obj_class, N_PROPERTIES, hev_sender_properties);

	g_type_class_add_private(klass, sizeof(HevSenderPrivate));
}

static void hev_sender_initable_iface_init(GInitableIface * iface)
{
	iface->init = hev_sender_initable_init;
}

static void hev_sender_init(HevSender * self)
{
	HevSenderPrivate * priv = HEV_SENDER_GET_PRIVATE(self);
}

static gboolean hev_sender_initable_init(GInitable * initable,
					GCancellable * cancellable, GError ** error)
{
	HevSender * self = HEV_SENDER(initable);
	HevSenderPrivate * priv = HEV_SENDER_GET_PRIVATE(self);
	gchar errbuf[PCAP_ERRBUF_SIZE];

	if(NULL == priv->interface)
	{
		g_set_error(error, g_quark_from_string("hev-sender"), 1, "Interface is NULL!");
		return FALSE;
	}

	priv = HEV_SENDER_GET_PRIVATE(self);
	priv->pcap = pcap_open_live(priv->interface, HEV_SENDER_BUFFER_SIZE,
			1, HEV_SENDER_TIMEOUT, errbuf);
	if(NULL == priv->pcap)
	{
		g_set_error(error, g_quark_from_string("hev-sender"), 2, "%s", errbuf);
		return FALSE;
	}
	pcap_breakloop(priv->pcap);

	return TRUE;
}

static void hev_sender_set_property(GObject * obj, guint id,
				const GValue * value, GParamSpec * pspec)
{
	HevSender * self = HEV_SENDER(obj);
	HevSenderPrivate * priv = HEV_SENDER_GET_PRIVATE(self);

	switch(id)
	{
	case PROP_INTERFACE:
		{
			g_free(priv->interface);
			priv->interface = g_strdup(g_value_get_string(value));
			g_object_notify(obj, "interface");
			break;
		}
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, pspec);
		break;
	}
}

static void hev_sender_get_property(GObject * obj, guint id,
				GValue * value, GParamSpec * pspec)
{
	HevSender * self = HEV_SENDER(obj);
	HevSenderPrivate * priv = HEV_SENDER_GET_PRIVATE(self);

	switch(id)
	{
	case PROP_INTERFACE:
		{
			if(priv->interface)
			{
				g_value_set_string(value, priv->interface);
			}
			else
			{
				g_value_set_string(value, "");
			}
			break;
		}
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, pspec);
		break;
	}
}

GObject * hev_sender_new(GCancellable * cancellable, GError ** error, gchar * interface)
{
	return g_initable_new(HEV_TYPE_SENDER, cancellable, error, "interface", interface, NULL);
}

gboolean hev_sender_send_find_server(HevSender * self, guchar * local_mac)
{
	HevSenderPrivate * priv = NULL;
	static guint8 packet[0x22] =
	{
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* dest mac */
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* source mac */
		0x88, 0x8E, /* protocol IEEE 802.1x */
		0x01, 0x01, /* EAP start */
		0x00, 0x00, 0x6C, 0x69, 0x6E, 0x6B,
		0x61, 0x67, 0x65, 0x00, 0x00, 0xE3,
		0x00, 0x1B, 0x00, 0x00, 0x00, 0x00
	};

	g_return_val_if_fail(HEV_IS_SENDER(self), FALSE);
	g_return_val_if_fail(local_mac, FALSE);

	priv = HEV_SENDER_GET_PRIVATE(self);

	g_return_val_if_fail(priv->pcap, FALSE);

	g_memmove(packet+6, local_mac, ETH_ALEN); /* set source mac */

	//g_debug("Sending find server packet...");
	if(0 != pcap_sendpacket(priv->pcap, packet, 0x22))
	{
		g_warning("[%s -> pcap_sendpacket]", __FUNCTION__);
		return FALSE;
	}

	return TRUE;
}

gboolean hev_sender_send_account_name(HevSender * self, guchar * local_mac,
		guchar * remote_mac, guint8 id, gchar * name)
{
	HevSenderPrivate * priv = NULL;
	static guint8 packet[0x3B] =
	{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* dest mac */
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* source mac */
		0x88, 0x8E, /* protocol IEEE 802.1x */
		0x01, 0x00, /* EAP packet */
		0x00, 0x12, 0x02, 0x6A, 0x00, 0x12,
		0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00
	};
	static guint8 linkage[0x10] =
	{
		0x6C, 0x69, 0x6E, 0x6B, 0x61, 0x67,
		0x65, 0x00, 0x00, 0xE3, 0x00, 0x1B,
		0x00, 0x00, 0x00, 0x00
	};
	gint name_len = 0;

	g_return_val_if_fail(HEV_IS_SENDER(self), FALSE);
	g_return_val_if_fail(local_mac, FALSE);
	g_return_val_if_fail(remote_mac, FALSE);
	g_return_val_if_fail(name, FALSE);

	priv = HEV_SENDER_GET_PRIVATE(self);

	g_return_val_if_fail(priv->pcap, FALSE);

	g_memmove(packet, remote_mac, ETH_ALEN); /* set dest mac */
	g_memmove(packet+6, local_mac, ETH_ALEN); /* set source mac */
	packet[0x13] = id; /* id from received packet */
	name_len = strlen(name);
	g_memmove(packet+0x17, name, name_len); /* set account name */
	*(gshort*)(packet+0x10) = g_htons((gshort)(name_len+5)); /* length */
	*(gshort*)(packet+0x14) = *(gshort*)(packet+0x10); /* length */
	g_memmove(packet+0x17+name_len, linkage, 0x10); /* what's this? */

	//g_debug("Sending account name packet...");
	if(0 != pcap_sendpacket(priv->pcap, packet, 0x3B))
	{
		g_warning("[%s -> pcap_sendpacket]", __FUNCTION__);
		return FALSE;
	}

	return TRUE;
}

gboolean hev_sender_send_account_passwd(HevSender * self, guchar * local_mac,
		guchar * remote_mac, guint8 id, guint8 type, guint8 * pkey, gchar * passwd)
{
	HevSenderPrivate * priv = NULL;
	static guint8 packet[0x38] =
	{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* dest mac */
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* source mac */
		0x88, 0x8E, /* protocol IEEE 802.1x */
		0x01, 0x00, /* EAP packet */
		0x00, 0x16, 0x02, 0x6A, 0x00, 0x16,
		0x99, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x6C, 0x69, 0x6E, 0x6B, 0x61, 0x67,
		0x65, 0x00, 0x00, 0xE3, 0x00, 0x1B,
		0x00, 0x00, 0x00, 0x00
	};

	g_return_val_if_fail(HEV_IS_SENDER(self), FALSE);
	g_return_val_if_fail(local_mac, FALSE);
	g_return_val_if_fail(remote_mac, FALSE);
	g_return_val_if_fail(pkey, FALSE);
	g_return_val_if_fail(passwd, FALSE);

	priv = HEV_SENDER_GET_PRIVATE(self);

	g_return_val_if_fail(priv->pcap, FALSE);

	g_memmove(packet, remote_mac, ETH_ALEN); /* set dest mac */
	g_memmove(packet+6, local_mac, ETH_ALEN); /* set source mac */
	packet[0x13] = id; /* id from received packet */
	packet[0x16] = type; /* type from received packet */
	*(gshort*)(packet+0x10) = g_htons(22);
	*(gshort*)(packet+0x14) = *(gshort*)(packet+0x10);

	/* encrypt passwd by linkage, oh, my god!! */
	{
		guchar code[17];
		guint8 digest[16];
		gint i = 0, sum = 0, di = 0, passwd_len = 0;
		gsize digest_len = 16;
		GChecksum * checksum = NULL;
		static guchar pubkey[0x100] =
		{
			0x75, 0x73, 0x65, 0x20, 0x6D, 0x64, 0x35, 0x20,
			0x65, 0x72, 0x72, 0x6F, 0x72, 0x21, 0x21, 0x00,
			0x8B, 0xF4, 0x6A, 0x01, 0xA1, 0x00, 0x2C, 0x43,
			0x00, 0x50, 0xFF, 0x15, 0x00, 0x68, 0x43, 0x00,
			0x3B, 0xF4, 0xE8, 0x70, 0xB7, 0xFF, 0xFF, 0x8B,
			0xF4, 0x6A, 0x01, 0xA1, 0x8C, 0x2D, 0x43, 0x00,
			0x50, 0xFF, 0x15, 0x00, 0x68, 0x43, 0x00, 0x3B,
			0xF4, 0xE8, 0x59, 0xB7, 0xFF, 0xFF, 0x8B, 0xF4,
			0x6A, 0x01, 0xA1, 0x98, 0x30, 0x43, 0x00, 0x50,
			0xFF, 0x15, 0x00, 0x68, 0x43, 0x00, 0x3B, 0xF4,
			0xE8, 0x42, 0xB7, 0xFF, 0xFF, 0x8B, 0xF4, 0x6A,
			0x01, 0xA1, 0x10, 0x2C, 0x43, 0x00, 0x50, 0xFF,
			0x15, 0x00, 0x68, 0x43, 0x00, 0x3B, 0xF4, 0xE8,
			0x2B, 0xB7, 0xFF, 0xFF, 0x8B, 0xF4, 0x6A, 0x01,
			0xA1, 0x04, 0x2C, 0x43, 0x00, 0x50, 0xFF, 0x15,
			0x00, 0x68, 0x43, 0x00, 0x3B, 0xF4, 0xE8, 0x14,
			0xB7, 0xFF, 0xFF, 0x8B, 0xF4, 0x6A, 0x01, 0xA1,
			0x90, 0x2D, 0x43, 0x00, 0x50, 0xFF, 0x15, 0x00,
			0x68, 0x43, 0x00, 0x3B, 0xF4, 0xE8, 0xFD, 0xB6,
			0xFF, 0xFF, 0x8B, 0xF4, 0x6A, 0x01, 0xA1, 0xF0
		};

		passwd_len = strlen(passwd);
		g_memmove(code, pkey, 16);
		code[16] = id;
		checksum = g_checksum_new(G_CHECKSUM_MD5);
		g_checksum_update(checksum, code, 17);
		g_checksum_get_digest(checksum, digest, &digest_len);
		g_checksum_free(checksum);

		for(i=0; i<0x10; i++)
		{
			sum += pkey[i];
		}
		di = sum % 10;
		di <<= 4;
		for(i=0; i<16; i++)
		{
			digest[i] ^= pubkey[di+i];
		}
		for(i=0; i<passwd_len; i++)
		{
			digest[i] ^= passwd[i];
		}
		packet[0x17] = 18;
		g_memmove(packet+0x18, digest, 16);
	}

	//g_debug("Sending account passwd packet...");
	if(0 != pcap_sendpacket(priv->pcap, packet, 0x38))
	{
		g_warning("[%s -> pcap_sendpacket]", __FUNCTION__);
		return FALSE;
	}

	return TRUE;
}

gboolean hev_sender_send_logout(HevSender * self, guchar * local_mac, guchar * remote_mac)
{
	HevSenderPrivate * priv = NULL;
	static guint8 packet[0x22] =
	{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* dest mac */
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* source mac */
		0x88, 0x8E, /* protocol IEEE 802.1x */
		0x01, 0x02, /* EAP logout */
		0x00, 0x00, 0x6C, 0x69, 0x6E, 0x6B,
		0x61, 0x67, 0x65, 0x00, 0x00, 0xC9,
		0x00, 0x19, 0x30, 0x00, 0x00, 0x01
	};

	g_return_val_if_fail(HEV_IS_SENDER(self), FALSE);
	g_return_val_if_fail(local_mac, FALSE);
	g_return_val_if_fail(remote_mac, FALSE);

	priv = HEV_SENDER_GET_PRIVATE(self);

	g_return_val_if_fail(priv->pcap, FALSE);

	g_memmove(packet, remote_mac, ETH_ALEN);
	g_memmove(packet+6, local_mac, ETH_ALEN);

	//g_debug("Sending logout packet...");
	if(0 != pcap_sendpacket(priv->pcap, packet, 0x22))
	{
		g_warning("[%s -> pcap_sendpacket]", __FUNCTION__);
		return FALSE;
	}

	return TRUE;
}

